#include "cem/parameter_reader.h"
#include "cem/boundary_condition.h"

#include <gtest/gtest.h>

#include <string>
#include <unordered_map>
#include <vector>
#include <fstream>

using cem::AnisotropySpecEntry;
using cem::ParameterReader;
using cem::BoundarySpec;
using cem::BoundaryType;

// 函数: build_example_config_path
// 作用: 构造 example/case1/input.json 的绝对路径, 该路径用于驱动参数解析测试.
// 参数: 无
// 返回: 以编译期定义 PROJECT_SOURCE_DIR 为前缀的完整文件路径
static std::string
build_example_config_path()
{
    std::string base = PROJECT_SOURCE_DIR; // 由 CMake 通过 target_compile_definitions 注入
    if (!base.empty() && base.back() == '/')
    {
        base.pop_back();
    }
    return base + "/example/case1/input.json";
}

// 函数: expect_map_value_near
// 作用: 辅助断言, 检查一个 (int->double) 映射在给定键处的值与期望值近似相等.
// 参数: m - 被测映射; key - 键; expected - 期望值; tol - 容差
// 返回: 无 (断言失败会导致测试失败)
static void
expect_map_value_near(const std::unordered_map<int, double> & m,
                      int key,
                      double expected,
                      double tol = 1e-12)
{
    auto it = m.find(key);
    ASSERT_NE(it, m.end()) << "map does not contain key: " << key;
    EXPECT_NEAR(it->second, expected, tol);
}

// 函数: expect_aniso_entry
// 作用: 辅助断言, 检查各向异性规格表在区域 key 处的主值与欧拉角是否匹配.
// 参数: m - 被测各向异性表; key - 区域 id; p1/p2/p3 - 主值; s/d/sl - 欧拉角(度)
// 返回: 无 (断言失败会导致测试失败)
static void
expect_aniso_entry(const std::unordered_map<int, AnisotropySpecEntry> & m,
                   int key,
                   double p1,
                   double p2,
                   double p3,
                   double s,
                   double d,
                   double sl,
                   double tol = 1e-12)
{
    auto it = m.find(key);
    ASSERT_NE(it, m.end()) << "anisotropy map does not contain area: " << key;
    const auto & e = it->second;
    EXPECT_EQ(e.area_id, key);
    EXPECT_NEAR(e.principal_1, p1, tol);
    EXPECT_NEAR(e.principal_2, p2, tol);
    EXPECT_NEAR(e.principal_3, p3, tol);
    EXPECT_NEAR(e.strike_deg, s, tol);
    EXPECT_NEAR(e.dip_deg, d, tol);
    EXPECT_NEAR(e.slant_deg, sl, tol);
}

// 函数: write_text_file
// 作用: 将给定文本内容写入到指定路径, 覆盖已存在内容, 用于在测试中生成临时 JSON 配置文件.
// 参数: path - 文件路径; text - 文件内容
// 返回: 无 (断言保证写入成功)
static void write_text_file(const std::string & path, const std::string & text)
{
    std::ofstream ofs(path, std::ios::binary);
    ASSERT_TRUE(ofs.is_open()) << "failed to open file for writing: " << path;
    ofs << text;
    ofs.close();
}

// 函数: build_temp_json_path
// 作用: 构造当前工作目录下的临时 JSON 文件路径.
// 参数: fname - 文件名
// 返回: 拼接后的路径
static std::string build_temp_json_path(const std::string & fname)
{
    return std::string("./") + fname;
}



// 用例: ParameterReader 能解析 BOUNDARY_CONDITIONS(新 schema 内含 name+ids) 并展开为具体的边界规格
TEST(ParameterReaderBoundaryTest, parse_inline_name_ids_and_expand_conditions)
{
    const std::string tmp_path = build_temp_json_path("tmp_boundary_config.json");

    // 最小 JSON: 直接在 BOUNDARY_CONDITIONS 项中提供 name 与 ids
    const std::string json_text = R"JSON({
        "BOUNDARY_CONDITIONS": [
            {"name": "top",    "ids": [1, 2], "type": "Dirichlet", "value": 5.0},
            {"name": "bottom", "ids": [3],    "type": "Impedance", "alpha": 123.0}
        ]
    })JSON";

    write_text_file(tmp_path, json_text);

    ParameterReader reader;
    ASSERT_TRUE(reader.load_from_file(tmp_path));

    // 展开后的规格: 应包括 id=1,2 的 Dirichlet(value=5) 与 id=3 的 Impedance(value=123)
    const auto & specs = reader.boundary_specs();
    ASSERT_EQ(specs.size(), 3u);

    // 将规格映射到 boundary_id -> (type, value)
    std::unordered_map<int, std::pair<BoundaryType, double>> spec_map;
    for (const auto & s : specs)
    {
        spec_map[s.boundary_id] = std::make_pair(s.type, s.value);
    }

    ASSERT_TRUE(spec_map.count(1));
    ASSERT_TRUE(spec_map.count(2));
    ASSERT_TRUE(spec_map.count(3));

    EXPECT_EQ(spec_map[1].first, BoundaryType::Dirichlet);
    EXPECT_EQ(spec_map[2].first, BoundaryType::Dirichlet);
    EXPECT_EQ(spec_map[3].first, BoundaryType::Impedance);

    EXPECT_DOUBLE_EQ(spec_map[1].second, 5.0);
    EXPECT_DOUBLE_EQ(spec_map[2].second, 5.0);
    EXPECT_DOUBLE_EQ(spec_map[3].second, 123.0);
}

// 用例: ParameterReader 能正确加载 example/case1/input.json 并解析各类参数
TEST(ParameterReaderTest, load_example_case1_and_parse_all)
{
    const std::string config_path = build_example_config_path();

    ParameterReader reader;
    // 加载 JSON 文件
    ASSERT_TRUE(reader.load_from_file(config_path));

    // 1) PHYSICAL_AREA/EPSL
    const auto & epsl_map = reader.area_epsl_map();
    ASSERT_EQ(epsl_map.size(), 2u);
    expect_map_value_near(epsl_map, 1, 1.00059, 1e-8);
    expect_map_value_near(epsl_map, 2, 4.0);

    // 2) PHYSICAL_AREA/COND
    const auto & cond_map = reader.area_cond_map();
    ASSERT_EQ(cond_map.size(), 2u);
    expect_map_value_near(cond_map, 1, 1e-10);
    expect_map_value_near(cond_map, 2, 1e-2);

    // 3) PHYSICAL_AREA/MU
    const auto & mu_map = reader.area_mu_map();
    ASSERT_EQ(mu_map.size(), 2u);
    expect_map_value_near(mu_map, 1, 1.0);
    expect_map_value_near(mu_map, 2, 1.0);

    // 4) FREQUENCY 数组
    const auto & freqs = reader.get_frequencies();
    ASSERT_EQ(freqs.size(), 2u);
    EXPECT_DOUBLE_EQ(freqs[0], 10.0);
    EXPECT_DOUBLE_EQ(freqs[1], 20.0);

    // 5) 各向异性: 电导率 Anisotropicc
    const auto & aniso_c = reader.area_aniso_conductivity();
    ASSERT_EQ(aniso_c.size(), 1u);
    // JSON: area=2, sigma1/2/3=0.01, 角度全为0
    expect_aniso_entry(aniso_c, 2, 0.01, 0.01, 0.01, 0.0, 0.0, 0.0);

    // 6) 各向异性: 磁导率 Anisotropicm
    const auto & aniso_m = reader.area_aniso_magnetic();
    ASSERT_EQ(aniso_m.size(), 2u);
    expect_aniso_entry(aniso_m, 2, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0);
    expect_aniso_entry(aniso_m, 1, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0);

    // 7) 各向异性: 介电常数 Anisotropicd
    const auto & aniso_d = reader.area_aniso_dielectric();
    ASSERT_EQ(aniso_d.size(), 2u);
    expect_aniso_entry(aniso_d, 2, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0);
    expect_aniso_entry(aniso_d, 1, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0);
}

// 函数: main
// 作用: 提供 GoogleTest 测试入口。当未链接 gtest_main 时，由此函数作为程序入口运行所有测试。
// 参数: argc/argv - 命令行参数
// 返回: GoogleTest 运行结果返回码
int
main(int argc, char ** argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}